package org.reborn.demo

import org.reborn.engine.app._
import org.reborn.engine.input._
import org.reborn.engine.input.jinput._
import org.reborn.engine.enums._
import org.reborn.engine.model.mesh._

import org.reborn.engine.render.texture._

import net.java.games.input.{Mouse => JIMouse, Component}

import org.reborn.utils.niobuffer.BufferUtil
import org.reborn.math._


import com.jogamp.opengl.util._
import com.jogamp.opengl.util.awt.TextRenderer
import javax.media.opengl._
import javax.media.opengl.fixedfunc.GLLightingFunc
import javax.media.opengl.glu._
import java.awt._
import java.awt.event._

object Textures {
  def main(args:Array[String]){
	
	println("java.library.path = "+System.getProperty("java.library.path"))
	  
    val options = new GLAppOptions()
    options.title = "Textures Test"
    options.fullscreen = false
    options.debugGL = true
    
	new Textures().startApp(options)
  }
}

class Textures extends GLApp {
  
  import GL._
  import GL2GL3._
  import GL2ES2._

  var dt = 0.0f;
  
  var rotX = 0.0f;
  var rotY = 0.0f;
  var zoom = 0.0f;
  
  var tex1D:Texture1D = null
  var tex2D:Texture2D = null
  var tex3D:Texture3D = null
  var texCubeMap:TextureCubeMap = null
  
  val text = new TextRenderer(new Font("SansSerif", Font.BOLD, 20))
  
  var cubeMeshId = -1;
  var sphereMeshId = -1;
  var torusMeshId = -1;
  var cube3DMeshId = -1;
  var sphere3DMeshId = -1;
  var torus3DMeshId = -1;
  var meshId = -1
  var mesh3DId = -1
  var displaying = "cube"

  var fpsAvg = 0.0f;
  
  var secs = 0.0f
  
  var wire = false;
  
  override def initScene(drawable:GLAutoDrawable){
    val gl = drawable.getGL.getGL2
    import gl._
    
    import GLLightingFunc._
    
    glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LEQUAL)
    glHint(GL2ES1.GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST)
    glClearColor(0.3f,0.3f,0.3f,1.0f)
    
    //Enable lighting
    glEnable(GL_LIGHTING)
    glShadeModel(GL_SMOOTH);
    glEnable(GL_COLOR_MATERIAL)
    glEnable(GL_LIGHT0)
    
    glLightfv(GL_LIGHT0,GL_POSITION,Array(1.0f,1.0f,1.0f,0.0f),0)
    glLightfv(GL_LIGHT0,GL_AMBIENT,Array(0.25f,0.25f,0.25f,1.0f),0)
    glLightfv(GL_LIGHT0,GL_DIFFUSE,Array(1.0f,1.0f,1.0f,1.0f),0)
    glLightfv(GL_LIGHT0,GL_SPECULAR,Array(1.0f,1.0f,1.0f,1.0f),0)
    
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
    glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,Array(1.0f,1.0f,1.0f,1.0f),0)
    glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,Array(0.1f,0.1f,0.1f,0.0f),0)
    //glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,180.0f)
    
    
    //Create the Cube Mesh
    var mesh = MeshShapes.Cube(1.0f)
	cubeMeshId = util.meshRenderer.register(gl,mesh,UpdateFrequency.Rarely)
	
	mesh.replaceVertData(ArrayType.TexCoord1,3,mesh(ArrayType.Vertex).getDataArray)
	cube3DMeshId = util.meshRenderer.register(gl,mesh,UpdateFrequency.Rarely)
	
	//Create the Sphere Mesh
	mesh = MeshShapes.Sphere(36,36,0.5f)
	sphereMeshId = util.meshRenderer.register(gl,mesh,UpdateFrequency.Rarely)
	
	mesh.replaceVertData(ArrayType.TexCoord1,3,mesh(ArrayType.Vertex).getDataArray)
	sphere3DMeshId = util.meshRenderer.register(gl,mesh,UpdateFrequency.Rarely)

	//Create the Torus Mesh
	mesh = MeshShapes.Torus(36,36,0.15f,0.5f)
	torusMeshId = util.meshRenderer.register(gl,mesh,UpdateFrequency.Rarely)
	
	mesh.replaceVertData(ArrayType.TexCoord1,3,mesh(ArrayType.Vertex).getDataArray)
	torus3DMeshId = util.meshRenderer.register(gl,mesh,UpdateFrequency.Rarely)

	
	meshId = cubeMeshId
	mesh3DId = cube3DMeshId
	
	setupTextures(gl)
  }
  
  protected def setupTextures(gl:GL){
    import gl._
    
	//The 1D Texture
	val tex1DBuffer = BufferUtil.createDirectFloatBuffer(128 * 3)
    for(i <- 0 until 128){
      val frac = i.asInstanceOf[Float] / 64.asInstanceOf[Float]
      tex1DBuffer.put(0.5f + 0.5f * math.sin(math.Pi * frac).asInstanceOf[Float])
      tex1DBuffer.put(0.5f + 0.5f * math.sin(2.0 * math.Pi * frac).asInstanceOf[Float])
      tex1DBuffer.put(0.5f + 0.5f * math.sin(4.0 * math.Pi * frac).asInstanceOf[Float])
    }
    glEnable(GL_TEXTURE_1D);
	tex1D = TextureManager.Texture1D(gl,Array(tex1DBuffer),128,GL_RGB,GL_FLOAT)
	
	//The 2D Texture
	val tex2DBuffer = BufferUtil.createDirectFloatBuffer(128 * 128 * 3)
    for(i <- -64 until 64;
        j <- -64 until 64){
      val point = VectorF(i.asInstanceOf[Float]/16.0f,j.asInstanceOf[Float]/16.0f)
      val dist = point.magnitude
      tex2DBuffer.put(0.5f + 0.5f * math.sin(math.Pi * dist).asInstanceOf[Float])
      tex2DBuffer.put(0.5f + 0.5f * math.sin(2.0 * math.Pi * dist).asInstanceOf[Float])
      tex2DBuffer.put(0.5f + 0.5f * math.sin(4.0 * math.Pi * dist).asInstanceOf[Float])
    }
    glEnable(GL_TEXTURE_2D);
    tex2D = TextureManager.Texture2D(gl,Array(tex2DBuffer),128,128,GL_RGB,GL_FLOAT)
	
	//The CubeMap Texture
	glEnable(GL_TEXTURE_CUBE_MAP);
    val cubeMapBuffers = Array(tex2DBuffer,tex2DBuffer,tex2DBuffer,tex2DBuffer,tex2DBuffer,tex2DBuffer)
    texCubeMap = TextureManager.TextureCubeMap(gl,cubeMapBuffers,128,128,GL_RGB,GL_FLOAT)
	
	//The 3D Texture
	val tex3DBuffer = BufferUtil.createDirectFloatBuffer(128 * 128 * 128 * 3)
    for(i <- -64 until 64;
        j <- -64 until 64;
        k <- -64 until 64){
      val point = VectorF(i.asInstanceOf[Float]/16.0f,j.asInstanceOf[Float]/16.0f,k.asInstanceOf[Float]/16.0f)
      val dist = point.magnitude
      tex3DBuffer.put(0.5f + 0.5f * math.sin(math.Pi * dist).asInstanceOf[Float])
      tex3DBuffer.put(0.5f + 0.5f * math.sin(2.0 * math.Pi * dist).asInstanceOf[Float])
      tex3DBuffer.put(0.5f + 0.5f * math.sin(4.0 * math.Pi * dist).asInstanceOf[Float])
    }
    glEnable(GL_TEXTURE_3D);
	tex3D = TextureManager.Texture3D(gl,Array(tex3DBuffer),128,128, 128,GL_RGB,GL_FLOAT)
  }
  
  override def updateLogic(seconds:Float){
    this.dt = seconds

    val mouse = input.mouse
    val keyboard = input.keyboard
    
    fpsAvg = ((25.0f * fpsAvg) + seconds) / 26.0f
    
    if(mouse.leftButton.pressed)
    {
    	rotY += 1.0f * mouse.changeX.value
    	rotX += 1.0f * mouse.changeY.value
    }
    
    if(keyboard.keydown(Keyboard.Keys.S)){
      meshId = sphereMeshId
      mesh3DId = sphere3DMeshId
      displaying = "sphere"
    }
      
    if(keyboard.keydown(Keyboard.Keys.C)){
      meshId = cubeMeshId
      mesh3DId = cube3DMeshId
      displaying = "cube"
    }
    
    if(keyboard.keydown(Keyboard.Keys.T)){
      meshId = torusMeshId
      mesh3DId = torus3DMeshId
      displaying = "torus"
    }
    	
      
    
    wire = mouse.rightButton.pressed
   
    secs+=dt
  }
  
  override def renderScene(drawable:GLAutoDrawable){
    val gl = drawable.getGL.getGL2
    import gl._
    
    import GLLightingFunc._
    
    val shininess = (31.0f * math.cos(secs).asInstanceOf[Float]) + 96.0f
    math.max(math.min(128.0f,shininess),0.0f)
    glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,shininess)
    
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)
    
    if(wire)
    	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    else
    	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    
    //Render the cube with 1D texture
    glEnable(GL_TEXTURE_1D);
    tex1D.bind(gl)
    glLoadIdentity();
    glTranslatef(-0.95f,0.95f,-5.0f + zoom)
    glRotatef(rotY,0.0f,1.0f,0.0f)
    glRotatef(rotX,1.0f,0.0f,0.0f)
    glColor3f(1.0f,1.0f,1.0f)
    util.meshRenderer.render(gl,meshId)
    glDisable(GL_TEXTURE_1D);

    //Render the cube with 2D texture
    glEnable(GL_TEXTURE_2D);
    tex2D.bind(gl)
    glLoadIdentity();
    glTranslatef(0.95f,0.95f,-5.0f + zoom)
    glRotatef(rotY,0.0f,1.0f,0.0f)
    glRotatef(rotX,1.0f,0.0f,0.0f)
    glColor3f(1.0f,1.0f,1.0f)
    util.meshRenderer.render(gl,meshId)
    glDisable(GL_TEXTURE_2D);

    
    import GL2._
    import GL2ES1._
    import javax.media.opengl.fixedfunc.GLMatrixFunc._
    //Render the cube with Cube Map texture
    glEnable(GL_TEXTURE_CUBE_MAP);
    texCubeMap.bind(gl)
    glLoadIdentity();
    glTranslatef(-0.95f,-0.95f,-5.0f + zoom)
    glRotatef(rotY,0.0f,1.0f,0.0f)
    glRotatef(rotX,1.0f,0.0f,0.0f)
    glColor3f(1.0f,1.0f,1.0f)
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
	glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
	glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
	glEnable(GL_TEXTURE_GEN_S);
	glEnable(GL_TEXTURE_GEN_T);
	glEnable(GL_TEXTURE_GEN_R);
    util.meshRenderer.render(gl,meshId)
   	glDisable(GL_TEXTURE_GEN_S);
	glDisable(GL_TEXTURE_GEN_T);
	glDisable(GL_TEXTURE_GEN_R);
    glDisable(GL_TEXTURE_CUBE_MAP);

    //Render the cube with 3D texture
    glEnable(GL_TEXTURE_3D);
    tex3D.bind(gl)
    glLoadIdentity();
    glTranslatef(0.95f,-0.95f,-5.0f + zoom)
    glRotatef(rotY,0.0f,1.0f,0.0f)
    glRotatef(rotX,1.0f,0.0f,0.0f)
    glColor3f(1.0f,1.0f,1.0f)
	glMatrixMode(GL_TEXTURE)
	glTranslatef(0.5f,0.5f,0.5f + secs * 0.5f)
    glMatrixMode(GL_MODELVIEW)
    util.meshRenderer.render(gl,mesh3DId)
    glMatrixMode(GL_TEXTURE)
	glLoadIdentity()
    glMatrixMode(GL_MODELVIEW)
    glDisable(GL_TEXTURE_3D);
    
    
    if(wire)
    	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    	
    //Render the FPS text
    val mouse = input.mouse
    val keyboard = input.keyboard
    text beginRendering(drawable.getWidth, drawable.getHeight)
    text setColor(1.0f, 1.0f, 1.0f, 1.0f);
    text draw("" + 1.0f/fpsAvg,0,0);
    text draw("Object: " + displaying,0,18);
    
    text draw("1D Texture",48,drawable.getHeight - 16);
    text draw("2D Texture",48 + drawable.getWidth / 2,drawable.getHeight - 16);
    text draw("Cube Map Texture",48,drawable.getHeight / 2 - 16);
    text draw("3D Texture",48 + drawable.getWidth / 2,drawable.getHeight / 2 - 16);
    
    text endRendering
      
    glFlush
  }
  
  override def deInitScene(drawable:GLAutoDrawable){
	TextureManager.DeleteAllTextures(drawable.getGL)
  }
}